package util;

import itecs.Itecs;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

import models.SLA;

import org.cloudbus.cloudsim.distributions.ExponentialDistr;
import org.cloudbus.cloudsim.distributions.ParetoDistr;
import org.cloudbus.cloudsim.distributions.UniformDistr;

public class MathUtils {


	public static double factorial(int n){

		double fact=1;

		for (int i=1; i<=n; i++){
			fact=fact*i;
		}

		return fact;
	}


	public static double integral(double a, double b, double n){

		double res;
		double x = 0;
		double y = 0;

		double area = (func(a)-func(b))/2;
		double h = (b-a)/n;

		for(double i = 1; i < n; i++){	
			x = a + i*h;
			y = func(x);
			area = area + y;			
		}

		res = area*h;

		return res;	
	}


	public static double func(double x){
		return Math.pow(x, 3);
	}



	/**
	 * For service time
	 * @param lambda
	 * @param length
	 * @return
	 */
	public static ArrayList<Double> getExponentialServiceTimes(long seed, double lambda, int length) {
		ArrayList<Double> instants= new ArrayList<Double>();
		double mean=1/lambda;
		ExponentialDistr ed = new ExponentialDistr(seed, mean);
		while(instants.size()<length){
			instants.add(ed.sample());
			//System.out.println("exponential service: " + instants.get(instants.size()-1));
		}

		double cumulative = 0;
		double min = Double.MAX_VALUE, max = 0;


		for(int i = 0; i < instants.size(); i++){
			cumulative += instants.get(i).doubleValue();
			//System.out.println("Theoric mean (fwe(l)):" + mean + " Value: " + instants.get(i).doubleValue() + " lambda: " + lambda + " Variance " + 1/(lambda * lambda));
			if(min > instants.get(i).doubleValue())
				min = instants.get(i).doubleValue();
			if(max < instants.get(i).doubleValue())
				max = instants.get(i).doubleValue();
		}

		//if(Globals.DEBUG_PROB)
			System.out.println("Theoric mean (exponential services):" + mean + " Campionary mean: " + cumulative/instants.size() + " mu: " + lambda + " Variance " + 1/(lambda * lambda));
		//System.out.println(min + " " + max);

		return instants;
	}


	/**
	 * for service time
	 * @param lambda1
	 * @param length
	 * @return
	 */
	public static ArrayList<Double> getParetoServiceTimes(long seed, double shape, double location,  int length) {

		ArrayList<Double> instants= new ArrayList<Double>();
		//ParetoDistr pd = new ParetoDistr(1000, 222);
		ParetoDistr pd = new ParetoDistr(new Random(seed), shape, location); // m:480, v: 9600

		double min = Double.MAX_VALUE, max = 0;

		while(instants.size()<length){
			instants.add(pd.sample());

			//System.out.println("pareto service: " + instants.get(instants.size()-1));
		}

		double cumulative = 0;

		for(int i = 0; i < instants.size(); i++){
			cumulative += instants.get(i).doubleValue();
			if(min > instants.get(i).doubleValue())
				min = instants.get(i).doubleValue();
			if(max < instants.get(i).doubleValue())
				max = instants.get(i).doubleValue();
		}


		//if(Globals.DEBUG_PROB)
			System.out.println("Theoric mean (pareto services):" + (shape*location)/(shape -1) + " Campionary mean: " + cumulative/instants.size() + " Variance " + (location*location*shape) / ((shape - 1)*(shape -1 )*(shape - 2)));
		//System.out.println(min + " " + max);
		return instants;
	}


	public static ArrayList<Double> getArrivals(long seed, double lambda) {
		ArrayList<Double> timeline = new ArrayList<Double>();
		double mean = 1/lambda;
		ExponentialDistr ed = new ExponentialDistr(seed, mean);

		double cumulative = ed.sample();
		while(cumulative < Globals.SIMULATION_TIME  ){
			timeline.add(cumulative);
			double sample = ed.sample();
			//System.out.println("interarrive: " + sample);
			//System.out.println("Theoric mean (fwe(l)):" + mean + " Value: " + sample + " lambda: " + lambda + " Variance " + 1/(lambda * lambda));
			cumulative = new Double(cumulative + sample);
		}

		if(Globals.DEBUG_PROB)
			System.out.println("Theoric mean (exponential arrivals):" + mean + " Campionary mean: " + timeline.get(timeline.size()-1)/timeline.size() + " lambda: " + lambda + " Variance " + 1/(lambda * lambda));

		return timeline;
	}



	public static GSLA calculateGSla(SLA sla){

		int seedIndex = 0;
		int m = 12;
		int start = 30;
		int end = 32;
		double jump = 1;
		double gSla = 0;
		String serviceType = "EXP";

		for(int l = start; l <= end; l+= jump){
			try {
				Globals.SIMULATION_TIME = 2*(2000/*Globals.SIMULATION_STABILIZATION_CLOUDLETS + Globals.SIMULATION_CLOUDLETS*/)/(l + l);
				m = Itecs.getM(Globals.FCFS_BROKER, m, sla, sla, l, l, 1/Globals.CLOUDLET_LENGTH_MEAN, Globals.SEEDS[seedIndex], serviceType);
				System.out.println(sla + "->" + m);
				
				if(m == -1){
					System.err.println("ERROR: gsla= " + m + " ABORTING!");
					return null;
				}
					
				
			} catch (Exception e) {
				e.printStackTrace();
			}
			gSla += m;
		}

		gSla/=(end - start);

		FileWriter file;
		try {
			file = new FileWriter("output/gslas.csv", true);
			file.write(sla + "\t"  + gSla + "\n");
			file.flush();
			file.close();
		} catch (IOException e) {
			e.printStackTrace();
		}


		return new GSLA(sla, gSla);
	}

	
	public static double calculateM(SLA sla, double lambda){

		int seedIndex = 0;
		int m = 12;
		double start = lambda;
		double end = lambda + 1;
		double jump = 1;
		String serviceType = "EXP";

		for(double l = start; l <= end; l+= jump){
			try {
				Globals.SIMULATION_TIME = 2*(2000/*Globals.SIMULATION_STABILIZATION_CLOUDLETS + Globals.SIMULATION_CLOUDLETS*/)/(l + l);
				m = Itecs.getM(Globals.FCFS_BROKER, m, sla, sla, l, l, 1/Globals.CLOUDLET_LENGTH_MEAN, Globals.SEEDS[seedIndex], serviceType);
				System.out.println(sla + "->" + m);
				
				if(m == -1){
					System.err.println("ERROR: gsla= " + m + " ABORTING!");
					return -1;
				}
					
				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return m;
	}
	

	public static double getGSLAFromFile(SLA sla){

		double gSla = - 1;
		BufferedReader bufferedReader = null;
		ArrayList<String> p = new ArrayList<String>();
		String line = null;
		int j = 0;
		String[] tokens = null;


		try {
			bufferedReader = new BufferedReader(new FileReader("gslas/gslas.csv"));
			while ((line = bufferedReader.readLine()) != null) {
				p.add(line);
				//System.out.println(p.get(j));
				j++;
			}
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}catch (IOException e1) {
			e1.printStackTrace();
		}

		for(int y = 0; y < p.size(); y++){			
			if(p.get(y).contains(sla.toString())){
				tokens = p.get(y).split("\t");
				gSla = Double.parseDouble(tokens[1]);
				break;
			}
		}

		System.out.println(sla + " " + gSla);


		return gSla;
	}


	public static void main(String[] args){

		FileWriter file;
		try {
			file = new FileWriter("gslas/gslas.csv", true);
			file.write("SLA" + "\t"  + "gSLA" + "\n");
			file.flush();
			file.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		double start = 3;
		double end = 8;
		int endingpoint = 30;
		UniformDistr ud1=new UniformDistr(start, end, Globals.SEEDS[0]);
		UniformDistr ud2=new UniformDistr(start, end, Globals.SEEDS[1]);
		double x1 = ud1.sample();
		double yarray[] = { 0.80, 0.85, 0.90, 0.95 };
		double y1 = yarray[(int) (ud1.sample() % yarray.length)];
		double x2 = ud2.sample();
		double y2 = yarray[(int) (ud2.sample() % yarray.length)];

		SLA slas[][] = new SLA[2][endingpoint];

		for (int i = 0; i < endingpoint; i++) {
			x1 = ud1.sample();
			y1 = yarray[(int) (ud1.sample() % yarray.length)];
			x2 = ud2.sample();
			y2 = yarray[(int) (ud2.sample()%yarray.length)];
			SLA sla1 = new SLA(x1, y1);
			SLA sla2 = new SLA(x2, y2);
			slas[0][i] = sla1;
			slas[1][i] = sla2;
		}
		
//		double[][] dedicated = new double[2][10]; 
//		double[][] shared = new double[2][10]; 

//		for(int i = 0; i < 10; i++){
//
//			System.out.println(slas[0][i] + " " + slas[1][i]);
//			//calculateGSla(slas[0][i]);
//			//calculateGSla(slas[1][i]);
//			dedicated[0][i] = calculateM(slas[0][i], 6);
//			dedicated[1][i] = calculateM(slas[1][i], 5);
//			
//			shared[0][i] = calculateM(slas[0][i], 11);
//			shared[1][i] = calculateM(slas[1][i], 11);
//
//		}

		for(int i = 0; i < 10; i++){
			//System.out.println(slas[0][i] + " " + slas[1][i]);
//			System.out.println("dedicated: =" +  (dedicated[0][i]+dedicated[1][i]));
//			System.out.println("shared: =" +  Math.max(shared[0][i], shared[1][i]));
			//System.out.println();
		}

		getParetoServiceTimes(10000, 5, 800, 10000);
		getParetoServiceTimes(10000, 2.3, 565.5, 10000);
		getExponentialServiceTimes(1234, 1.0/1000, 10000);


		
	}


}
